﻿#region License
/*
Copyright (c) 2005-2011, CellAO Team

All rights reserved.

Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:

    * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
    * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
    * Neither the name of the CellAO Team nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#endregion

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using AO.Core;
using System.Data;

namespace ZoneEngine
{
    /// <summary>
    /// Combined class for coordinates and heading
    /// </summary>
    public class coordheading
    {
        public AOCoord Coordinates = new AOCoord();
        public Quaternion Heading = new Quaternion(0, 0, 0, 0);
    }

    /// <summary>
    /// Class for static game objects (bank terminals, static shops etc)
    /// </summary>
    public class Statels
    {
        public static DataTable dt_functions;
        public static DataTable dt_events;
        public static DataTable dt_reqs;
        public static DataTable dt_args;
        public static int cnt_args = 0;
        public static int cnt_reqs = 0;
        public static int cnt_events = 0;
        public static int cnt_functions = 0;

        /// <summary>
        /// Statel event class
        /// </summary>
        public class Statel_Event
        {
            public int EventNumber = 0;
            public List<Statel_Function> Functions = new List<Statel_Function>();

            /// <summary>
            /// Load event functions
            /// </summary>
            /// <param name="eventid">Event ID</param>
            /// <param name="statelid">Statel ID</param>
            public void LoadFunctions(Int32 eventid, Int32 statelid)
            {
                while ((cnt_functions < dt_functions.Rows.Count) && ((Int32)dt_functions.Rows[cnt_functions][1] == eventid) && ((Int32)dt_functions.Rows[cnt_functions][2] == statelid))
                {
                    Statel_Function sf = new Statel_Function();
                    sf.FunctionNumber = (Int32)dt_functions.Rows[cnt_functions][3];
                    sf.Target = (Int32)dt_functions.Rows[cnt_functions][4];
                    sf.TickCount = (Int32)dt_functions.Rows[cnt_functions][5];
                    sf.TickInterval = (Int32)dt_functions.Rows[cnt_functions][6];
                    sf.LoadRequirements((Int32)dt_functions.Rows[cnt_functions][0], eventid, statelid);
                    sf.LoadArguments((Int32)dt_functions.Rows[cnt_functions][0], eventid, statelid);
                    Functions.Add(sf);
                    cnt_functions++;
                }
            }

            public override string ToString()
            {
                string s = "Eventnum: " + EventNumber + "\r\n";
                foreach (Statel_Function sf in Functions)
                {
                    s = s + sf.ToString();
                }
                return s;
            }
        }

        /// <summary>
        /// Statel function handler class
        /// </summary>
        public class Statel_Function
        {

            #region Operators
            ///
            /// Operators
            /// 
            const int operator_equalto = 0;
            const int operator_lessthan = 1;
            const int operator_greaterthan = 2;
            const int operator_or = 3;
            const int operator_and = 4;
            const int operator_time_less = 5;
            const int operator_time_larger = 6;
            const int operator_item_has = 7;
            const int operator_item_hasnot = 8;
            const int operator_id = 9;
            const int operator_targetid = 10;
            const int operator_targetsignal = 11;
            const int operator_targetstat = 12;
            const int operator_primary_item = 13;
            const int operator_secondary_item = 14;
            const int operator_area_zminmax = 15;
            const int operator_user = 16;
            const int operator_itemanim = 17;
            const int operator_ontarget = 18;
            const int operator_onself = 19;
            const int operator_signal = 20;
            const int operator_onsecondaryitem = 21;
            const int operator_bitand = 22;
            const int operator_bitor = 23;
            const int operator_unequal = 24;
            const int operator_illegal = 25;
            const int operator_onuser = 26;
            const int operator_onvalidtarget = 27;
            const int operator_oninvalidtarget = 28;
            const int operator_onvaliduser = 29;
            const int operator_oninvaliduser = 30;
            const int operator_haswornitem = 31;
            const int operator_hasnotwornitem = 32;
            const int operator_haswieldeditem = 33;
            const int operator_hasnotwieldeditem = 34;
            const int operator_hasformula = 35;
            const int operator_hasnotformula = 36;
            const int operator_ongeneralbeholder = 37;
            const int operator_isvalid = 38;
            const int operator_isinvalid = 39;
            const int operator_isalive = 40;
            const int operator_iswithinvicinity = 41;
            const int operator_not = 42;
            const int operator_iswithinweaponrange = 43;
            const int operator_isnpc = 44;
            const int operator_isfighting = 45;
            const int operator_isattacked = 46;
            const int operator_isanyonelooking = 47;
            const int operator_isfoe = 48;
            const int operator_isindungeon = 49;
            const int operator_issameas = 50;
            const int operator_distanceto = 51;
            const int operator_isinnofightingarea = 52;
            const int operator_template_compare = 53;
            const int operator_min_max_level_compare = 54;
            const int operator_monstertemplate = 57;
            const int operator_hasmaster = 58;
            const int operator_canexecuteformulaontarget = 59;
            const int operator_area_targetinvicinity = 60;
            const int operator_isunderheavyattack = 61;
            const int operator_islocationok = 62;
            const int operator_isnottoohighlevel = 63;
            const int operator_haschangedroomwhilefighting = 64;
            const int operator_kullnumberof = 65;
            const int operator_testnumpets = 66;
            const int operator_numberofitems = 67;
            const int operator_primarytemplate = 68;
            const int operator_isteleporting = 69;
            const int operator_isflying = 70;
            const int operator_scanforstat = 71;
            const int operator_hasmeonpetlist = 72;
            const int operator_trickledownlarger = 73;
            const int operator_trickledownless = 74;
            const int operator_ispetoverequipped = 75;
            const int operator_haspetpendingnanoformula = 76;
            const int operator_ispet = 77;
            const int operator_canattackchar = 79;
            const int operator_istowercreateallowed = 80;
            const int operator_inventoryslotisfull = 81;
            const int operator_inventoryslotisempty = 82;
            const int operator_candisabledefenseshield = 83;
            const int operator_isnpcornpccontrolledpet = 84;
            const int operator_sameasselectedtarget = 85;
            const int operator_isplayerorplayercontrolledpet = 86;
            const int operator_hasenterednonpvpzone = 87;
            const int operator_uselocation = 88;
            const int operator_isfalling = 89;
            const int operator_isondifferentplayfield = 90;
            const int operator_hasrunningnano = 91;
            const int operator_hasrunningnanoline = 92;
            const int operator_hasperk = 93;
            const int operator_isperklocked = 94;
            const int operator_isfactionreactionset = 95;
            const int operator_hasmovetotarget = 96;
            const int operator_isperkunlocked = 97;
            const int operator_true = 98;
            const int operator_false = 99;
            const int operator_oncaster = 100;
            const int operator_hasnotrunningnano = 101;
            const int operator_hasnotrunningnanoline = 102;
            const int operator_hasnotperk = 103;
            const int operator_notbitand = 107;
            const int operator_obtaineditem = 108;
            #endregion

            #region Targets
            ///
            /// Targets
            /// 
            const int itemtarget_user = 1;
            const int itemtarget_wearer = 2;
            const int itemtarget_target = 3;
            const int itemtarget_fightingtarget = 14;
            const int itemtarget_self = 19;
            const int itemtarget_selectedtarget = 23;
            #endregion


            public int FunctionNumber = 0;
            /// <summary>
            /// Function arguments list
            /// </summary>
            public List<string> Arguments = new List<string>();
            /// <summary>
            /// Function requirements list
            /// </summary>
            public List<Statel_Function_Requirement> Requirements = new List<Statel_Function_Requirement>();
            public int Target = 0;
            public int TickCount = 0;
            public int TickInterval = 0;

            /// <summary>
            /// Read function requirements
            /// </summary>
            /// <param name="functionid">Function ID</param>
            /// <param name="eventid">Event ID</param>
            /// <param name="statelid">Statel ID</param>
            public void LoadRequirements(int functionid, int eventid, int statelid)
            {
                while ((cnt_reqs < dt_reqs.Rows.Count) && ((Int32)dt_reqs.Rows[cnt_reqs][1] == functionid) && ((Int32)dt_reqs.Rows[cnt_reqs][2] == eventid) && ((Int32)dt_reqs.Rows[cnt_reqs][3] == statelid))
                {
                    Statel_Function_Requirement sfr = new Statel_Function_Requirement();
                    sfr.AttributeNumber = (Int32)dt_reqs.Rows[cnt_reqs][4];
                    sfr.AttributeValue = (Int32)dt_reqs.Rows[cnt_reqs][5];
                    sfr.Operator = (Int32)dt_reqs.Rows[cnt_reqs][6];
                    sfr.ChildOperator = (Int32)dt_reqs.Rows[cnt_reqs][7];
                    sfr.Target = (Int32)dt_reqs.Rows[cnt_reqs][8];
                    Requirements.Add(sfr);
                    cnt_reqs++;
                }
            }

            /// <summary>
            /// Read function arguments
            /// </summary>
            /// <param name="functionid">Function ID</param>
            /// <param name="eventid">Event ID</param>
            /// <param name="statelid">Statel ID</param>
            public void LoadArguments(int functionid, int eventid, int statelid)
            {
                while ((cnt_args < dt_args.Rows.Count) && ((Int32)dt_args.Rows[cnt_args][1] == functionid) && ((Int32)dt_args.Rows[cnt_args][2] == eventid) && ((Int32)dt_args.Rows[cnt_args][3] == statelid))
                {
                    Arguments.Add((string)dt_args.Rows[cnt_args][4]);
                    cnt_args++;
                }
            }

            public override string ToString()
            {
                string s = "Function " + FunctionNumber + " TickCount " + TickCount + " TickInterval " + TickInterval + " Target " + Target + "\r\n";
                foreach (string arg in Arguments)
                {
                    s = s + "Arg: " + arg + "\r\n";
                }
                return s;
            }

            public coordheading FindEntry(int Playfield, int DestinationNumber)
            {
                coordheading ret = new coordheading();
                ret.Coordinates.x = -1;
                foreach (Collision.WallCollision.Line l in Collision.WallCollision.destinations[Playfield].playfield.lines)
                {
                    if (l.ID != DestinationNumber)
                    {
                        continue;
                    }
                    ret.Coordinates.x = (l.start.X + l.end.X) / 2;
                    ret.Coordinates.y = (l.start.Y + l.end.Y) / 2;
                    ret.Coordinates.z = (l.start.Z + l.end.Z) / 2;
                    // TODO: Calculate the right Quaternion for the heading...
                    // - Algorithman
                    Quaternion q = new Quaternion(new Vector3((l.end.X - l.start.X), 1, -(l.end.Z - l.start.Z)));
                    ret.Heading.x = q.x;
                    ret.Heading.y = q.y;
                    ret.Heading.z = q.z;
                    ret.Heading.w = q.w;
                }
                return ret;
            }

            public void Execute(Client cli, Statel parent, int Eventnumber)
            {
                bool reqs_met = true;
                int childop = -1;
                Character ftarget = null;
                bool reqresult = true;
                Character chartarget = (Character)Misc.FindDynel.FindDynelByID(cli.Character.Target.Type, cli.Character.Target.Instance);

                for (int r = 0; r < Requirements.Count; r++)
                {
                    switch (Requirements[r].Target)
                    {
                        case itemtarget_user:
                            ftarget = cli.Character;
                            break;
                        case itemtarget_wearer:
                            ftarget = cli.Character;
                            break;
                        case itemtarget_target:
                            ftarget = chartarget;
                            break;
                        case itemtarget_fightingtarget:
                            // Fighting target
                            break;
                        case itemtarget_self:
                            ftarget = cli.Character;
                            break;
                        case itemtarget_selectedtarget:
                            ftarget = chartarget;
                            break;
                    }
                    if (ftarget == null)
                    {
                        reqs_met = false;
                        return;
                    }
                    int statval = ftarget.Stats.Get(Requirements[r].AttributeNumber);
                    switch (Requirements[r].Operator)
                    {
                        case operator_and:
                            reqresult = ((statval & Requirements[r].AttributeValue) != 0);
                            break;
                        case operator_or:
                            reqresult = ((statval | Requirements[r].AttributeValue) != 0);
                            break;
                        case operator_equalto:
                            reqresult = (statval == Requirements[r].AttributeValue);
                            break;
                        case operator_lessthan:
                            reqresult = (statval < Requirements[r].AttributeValue);
                            break;
                        case operator_greaterthan:
                            reqresult = (statval > Requirements[r].AttributeValue);
                            break;
                        case operator_unequal:
                            reqresult = (statval != Requirements[r].AttributeValue);
                            break;
                        case operator_true:
                            reqresult = (statval != 0);
                            break;
                        case operator_false:
                            reqresult = (statval == 0);
                            break;
                        case operator_bitand:
                            reqresult = ((statval & Requirements[r].AttributeValue) != 0);
                            break;
                        case operator_bitor:
                            reqresult = ((statval | Requirements[r].AttributeValue) != 0);
                            break;
                        default:
                            reqresult = true;
                            break;
                    }

                    switch (childop)
                    {
                        case operator_and:
                            reqs_met &= reqresult;
                            break;
                        case operator_or:
                            reqs_met |= reqresult;
                            break;
                        case -1:
                            reqs_met = reqresult;
                            break;
                        default:
                            break;
                    }
                    childop = Requirements[r].ChildOperator;
                }


                if (!reqs_met)
                {
                    cli.SendChatText("Requirements not met. (better errormessage not coded yet)");
                    return;
                }



                if (!Program.FunctionC.CallFunction(FunctionNumber, cli.Character, cli.Character, parent, Arguments.ToArray()))
                {
#if DEBUG
                    cli.SendChatText("Statel " + parent.Type.ToString() + ":" + parent.Instance.ToString() + " handling " + Eventnumber.ToString() + " Function " + FunctionNumber.ToString() + " " + cli.Character.Coordinates.ToString());
                    foreach (string arg in Arguments)
                    {
                        cli.SendChatText("Argument: " + arg);
                    }
#endif
                }
            }
        }

        public class Statel_Function_Requirement
        {
            public int AttributeNumber = 0;
            public int AttributeValue = 0;
            public int Operator = 0;
            public int ChildOperator = 255;
            public int Target = 0;
        }

        public static Dictionary<Int32, List<Statel>> Statelppf = new Dictionary<int, List<Statel>>();
        public static Dictionary<Int32, List<Statel>> StatelppfonEnter = new Dictionary<int, List<Statel>>();
        public static Dictionary<Int32, List<Statel>> StatelppfonUse = new Dictionary<int, List<Statel>>();

        public static int CacheAllStatels()
        {
            SqlWrapper ms = new SqlWrapper();
            int count = 0;
            DataTable dt = ms.ReadDT("SELECT * FROM statels ORDER BY id ASC");
            dt_args = ms.ReadDT("SELECT * FROM statel_function_arguments ORDER BY statel_id, event_id, function_id, attrid ASC");
            dt_events = ms.ReadDT("SELECT * FROM statel_events ORDER BY statel_id, eventid ASC");
            dt_reqs = ms.ReadDT("SELECT * FROM statel_function_reqs ORDER BY statel_id, event_id, function_id, reqid ASC");
            dt_functions = ms.ReadDT("SELECT * FROM statel_functions ORDER BY statel_id, event_id, functionid ASC");
            int maxcount = 0;
            ms.sqlclose();

            List<Statel> temp;
            maxcount = dt.Rows.Count;
            foreach (DataRow dr in dt.Rows)
            {
                int pf = (Int32)dr[0];
                if (Statelppf.ContainsKey(pf) == false)
                {
                    temp = new List<Statel>();
                    Statelppf.Add(pf, temp);
                    temp = new List<Statel>();
                    StatelppfonEnter.Add(pf, temp);
                    temp = new List<Statel>();
                    StatelppfonUse.Add(pf, temp);
                }
                Statel tempstatel = new Statel();
                tempstatel.Coordinates.x = (Single)dr[5];
                tempstatel.Coordinates.y = (Single)dr[6];
                tempstatel.Coordinates.z = (Single)dr[7];
                tempstatel.Type = (Int32)dr[1];
                tempstatel.Instance = (UInt32)dr[2];
                tempstatel.PlayField = (Int32)dr[0];
                tempstatel.Template = (Int32)dr[3];

                tempstatel.LoadEvents((Int32)dr[4]);
                Statelppf[pf].Add(tempstatel);

                foreach (Statel_Event e in tempstatel.Events)
                {
                    if ((e.EventNumber == ItemHandler.eventtype_onenter) || (e.EventNumber == ItemHandler.eventtype_ontargetinvicinity))
                    {
                        StatelppfonEnter[pf].Add(tempstatel);
                    }
                    if (e.EventNumber == ItemHandler.eventtype_onuse)
                    {
                        StatelppfonUse[pf].Add(tempstatel);
                    }
                }
                count++;
                if ((count % 10) == 0)
                {
                    Console.Write("\rReading statels: " + count.ToString() + "/" + maxcount.ToString() + "                        \r");
                }
            }

            ms.sqlclose();
            Console.Write("                                               \r");
            return count;
        }

        public class Statel
        {

            public List<Statel_Event> Events = new List<Statel_Event>();
            public uint Instance = 0;
            public int Type = 0;
            public int Template = 0;
            public int PlayField = 0;
            public AOCoord Coordinates = new AOCoord();

            public void LoadEvents(Int32 statelid)
            {

                while ((cnt_events < dt_events.Rows.Count) && ((Int32)dt_events.Rows[cnt_events][1] == statelid))
                {
                    Statel_Event e = new Statel_Event();
                    e.EventNumber = (Int32)dt_events.Rows[cnt_events][2];
                    e.LoadFunctions((Int32)dt_events.Rows[cnt_events][0], statelid);
                    Events.Add(e);
                    cnt_events++;
                }
            }


            public bool onEnter(Client cli)
            {
                foreach (Statel_Event e in Events)
                {
                    if (e.EventNumber != ItemHandler.eventtype_onenter)
                    {
                        continue;
                    }
                    if ((AOCoord.distance2D(cli.Character.Coordinates, Coordinates) < 0.8f) && (Math.Abs(cli.Character.Coordinates.y - Coordinates.y) < 5))
                    {
                        foreach (Statel_Function f in e.Functions)
                        {
                            f.Execute(cli, this, e.EventNumber);
                        }
                        return true;
                    }
                }
                return false;
            }

            public bool onTargetinVicinity(Client cli)
            {
                foreach (Statel_Event e in Events)
                {
                    if (e.EventNumber != ItemHandler.eventtype_ontargetinvicinity)
                    {
                        continue;
                    }
                    if ((AOCoord.distance2D(cli.Character.Coordinates, Coordinates) < 1.2f) && (Math.Abs(cli.Character.Coordinates.y - Coordinates.y) < 5))
                    {
                        foreach (Statel_Function f in e.Functions)
                        {
                            f.Execute(cli, this, e.EventNumber);
                        }
                        return true;
                    }
                }
                return false;
            }

            public bool onUse(Client cli, Identity target)
            {
                foreach (Statel_Event e in Events)
                {
                    if (e.EventNumber != ItemHandler.eventtype_onuse)
                    {
                        continue;
                    }
                    if (((UInt32)target.Instance != Instance) || (target.Type != Type))
                    {
                        continue;
                    }
                    foreach (Statel_Function f in e.Functions)
                    {
                        f.Execute(cli, this, e.EventNumber);
                    }
                    return true;
                }
                return false;
            }
        }
    }
}

